CloudFormation으로 CloudWatch Alarms를 설정해 보기
안녕하세요 클래스메소드 김재욱(Kim Jaewook) 입니다. 이번에는 CloudFormation으로 CloudWatch Alarms를 설정하고, Amazon SNS를 통해 경보를 이메일로 받아보는 과정까지 정리헤 봤습니다.
CloudFormation 작성
VPC 생성
AWSTemplateFormatVersion: "2010-09-09" Description: VPC Network set Metadata: "AWS::CloudFormation::Interface": ParameterGroups: - Label: default: "Network Configuration" Parameters: - VPCCIDR - PublicSubnetACIDR - PublicSubnetCCIDR - PrivateSubnetACIDR - PrivateSubnetCCIDR ParameterLabels: VPCCIDR: default: "VPC CIDR" PublicSubnetACIDR: default: "PublicSubnetA CIDR" PublicSubnetCCIDR: default: "PublicSubnetC CIDR" PrivateSubnetACIDR: default: "PrivateSubnetA CIDR" PrivateSubnetCCIDR: default: "PrivateSubnetC CIDR" #------------------------------------------------------------------- #Input VPC, Subnet Parameters #------------------------------------------------------------------- Parameters: VPCCIDR: Type: String Default: "10.0.0.0/16" PublicSubnetACIDR: Type: String Default: "10.0.0.0/24" PublicSubnetCCIDR: Type: String Default: "10.0.64.0/24" PrivateSubnetACIDR: Type: String Default: "10.0.128.0/24" PrivateSubnetCCIDR: Type: String Default: "10.0.192.0/24" #------------------------------------------------------------------- #Set VPC, InternetGateway, Subnet #------------------------------------------------------------------- Resources: VPC: Type: "AWS::EC2::VPC" Properties: CidrBlock: !Ref VPCCIDR EnableDnsSupport: "true" EnableDnsHostnames: "true" InstanceTenancy: default Tags: - Key: Name Value: !Sub "test-vpc" InternetGateway: Type: "AWS::EC2::InternetGateway" Properties: Tags: - Key: Name Value: !Sub "test-igw" InternetGatewayAttachment: Type: "AWS::EC2::VPCGatewayAttachment" Properties: InternetGatewayId: !Ref InternetGateway VpcId: !Ref VPC PublicSubnetA: Type: "AWS::EC2::Subnet" Properties: AvailabilityZone: "ap-northeast-1a" CidrBlock: !Ref PublicSubnetACIDR VpcId: !Ref VPC Tags: - Key: Name Value: !Sub "test-front-subnet-1a" PublicSubnetC: Type: "AWS::EC2::Subnet" Properties: AvailabilityZone: "ap-northeast-1c" CidrBlock: !Ref PublicSubnetCCIDR VpcId: !Ref VPC Tags: - Key: Name Value: !Sub "test-front-subnet-1c" PrivateSubnetA: Type: "AWS::EC2::Subnet" Properties: AvailabilityZone: "ap-northeast-1a" CidrBlock: !Ref PrivateSubnetACIDR VpcId: !Ref VPC Tags: - Key: Name Value: !Sub "test-application-subnet-1a" PrivateSubnetC: Type: "AWS::EC2::Subnet" Properties: AvailabilityZone: "ap-northeast-1c" CidrBlock: !Ref PrivateSubnetCCIDR VpcId: !Ref VPC Tags: - Key: Name Value: !Sub "test-application-subnet-1c" #------------------------------------------------------------------- #Route Tables #------------------------------------------------------------------- FRONTRTB : Type: "AWS::EC2::RouteTable" Properties: VpcId: !Ref VPC Tags: - Key: Name Value: !Sub "test-front-rtb" APPRTB1A: Type: "AWS::EC2::RouteTable" Properties: VpcId: !Ref VPC Tags: - Key: Name Value: !Sub "test-application-rtb-1a" APPRTB1C: Type: "AWS::EC2::RouteTable" Properties: VpcId: !Ref VPC Tags: - Key: Name Value: !Sub "test-application-rtb-1c" FRONTRTBroute: Type: "AWS::EC2::Route" Properties: RouteTableId: !Ref FRONTRTB DestinationCidrBlock: "0.0.0.0/0" GatewayId: !Ref InternetGateway #------------------------------------------------------------------- #Route Tables Subnet Association #------------------------------------------------------------------- FRONTRTBAssociation: Type: "AWS::EC2::SubnetRouteTableAssociation" Properties: SubnetId: !Ref PublicSubnetA RouteTableId: !Ref FRONTRTB FRONTRTB2Association: Type: "AWS::EC2::SubnetRouteTableAssociation" Properties: SubnetId: !Ref PublicSubnetC RouteTableId: !Ref FRONTRTB APPRTB1AAssociation: Type: "AWS::EC2::SubnetRouteTableAssociation" Properties: SubnetId: !Ref PrivateSubnetA RouteTableId: !Ref APPRTB1A APPRTB1CAssociation: Type: "AWS::EC2::SubnetRouteTableAssociation" Properties: SubnetId: !Ref PrivateSubnetC RouteTableId: !Ref APPRTB1C #------------------------------------------------------------------- #OutPut #------------------------------------------------------------------- Outputs: # VPC VPC: Value: !Ref VPC Export: Name: !Sub "test-vpc" VPCCIDR: Value: !Ref VPCCIDR Export: Name: !Sub "test-vpc-cidr" # Subnet PublicSubnetA: Value: !Ref PublicSubnetA Export: Name: !Sub "test-front-subnet-1a" PublicSubnetACIDR: Value: !Ref PublicSubnetACIDR Export: Name: !Sub "test-front-subnet-1a-cidr" PublicSubnetC: Value: !Ref PublicSubnetC Export: Name: !Sub "test-front-subnet-1c" PublicSubnetCCIDR: Value: !Ref PublicSubnetCCIDR Export: Name: !Sub "test-front-subnet-1c-cidr" PrivateSubnetA: Value: !Ref PrivateSubnetA Export: Name: !Sub "test-application-subnet-1a" PrivateSubnetACIDR: Value: !Ref PrivateSubnetACIDR Export: Name: !Sub "test-application-subnet-1a-cidr" PrivateSubnetC: Value: !Ref PrivateSubnetC Export: Name: !Sub "test-application-subnet-1c" PrivateSubnetCCIDR: Value: !Ref PrivateSubnetCCIDR Export: Name: !Sub "test-application-subnet-1c-cidr" # Route FRONTRTB: Value: !Ref FRONTRTB Export: Name: !Sub "test-front-rtb" APPRTB1A: Value: !Ref APPRTB1A Export: Name: !Sub "test-application-rtb-1a" APPRTB1C: Value: !Ref APPRTB1C Export: Name: !Sub "test-application-rtb-1c"
다음 코드를 이용해서 VPC를 생성합니다.
EC2 생성
AWSTemplateFormatVersion: "2010-09-09" Description: Create EC2 Metadata: "AWS::CloudFormation::Interface": ParameterGroups: - Label: default: "EC2 Configuration" Parameters: - BastionEC2KeyPair - AppEC2KeyPair ParameterLabels: BastionEC2KeyPair: default: "tokyo-ec2-key" AppEC2KeyPair: default: "AppEC2KeyPair" # ------------------------------------------------------------# # Input Parameters # ------------------------------------------------------------# Parameters: BastionEC2KeyPair: Type: String Default: "tokyo-ec2-key" AppEC2KeyPair: Type: String Default: "AppEC2KeyPair" # ------------------------------------------------------------# # Create Security Group # ------------------------------------------------------------# Resources: FrontBastionSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: "test-front-bastion-sg" GroupName: test-front-bastion-sg SecurityGroupIngress: IpProtocol: tcp FromPort : 22 ToPort : 22 CidrIp: 0.0.0.0/0 VpcId: { "Fn::ImportValue": !Sub "test-vpc" } Tags: - Key: "Name" Value: test-front-bastion-sg APPSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: "test-app-sg" GroupName: test-app-sg SecurityGroupIngress: - IpProtocol: tcp FromPort : 22 ToPort : 22 SourceSecurityGroupId: !GetAtt FrontBastionSecurityGroup.GroupId VpcId: { "Fn::ImportValue": !Sub "test-vpc" } Tags: - Key: "Name" Value: test-app-sg # ------------------------------------------------------------# # Create EC2 Instance # ------------------------------------------------------------# # Bastion EC2 BastionEC2: Type: AWS::EC2::Instance Properties: ImageId: ami-02c3627b04781eada InstanceType: t3.micro KeyName: !Ref BastionEC2KeyPair DisableApiTermination: true NetworkInterfaces: - AssociatePublicIpAddress: true DeviceIndex: "0" SubnetId: { "Fn::ImportValue": !Sub "test-front-subnet-1a" } GroupSet: - !Ref FrontBastionSecurityGroup Tags: - Key: "Name" Value: test-front-bastion # test-app-1a AppEC2: Type: AWS::EC2::Instance Properties: ImageId: ami-02c3627b04781eada InstanceType: t3.micro KeyName: !Ref AppEC2KeyPair DisableApiTermination: true SecurityGroupIds: - !Ref APPSecurityGroup SubnetId: { "Fn::ImportValue": !Sub "test-application-subnet-1a" } Tags: - Key: "Name" Value: test-app-1a
EC2 인스턴스를 생성합니다.
SNS Topic 생성
AWSTemplateFormatVersion: "2010-09-09" Description: Create sns # ------------------------------------------------------------# # Create sns Topic # ------------------------------------------------------------# Resources: MySNSTopic: Type: AWS::SNS::Topic Properties: TopicName: my_sns_topic Subscription: - Endpoint: [email protected] Protocol: email #------------------------------------------------------------------- #OutPut #------------------------------------------------------------------- Outputs: # SNS SNS: Value: !Ref MySNSTopic Export: Name: !Sub "my-sns-topic"
이메일로 알람을 받기 위해 Amazon SNS도 생성합니다.
Amazon SNS에 대한 이메일 설정은 아래 블로그를 참고해 주세요.
CloudWatch Alarms 생성
AWSTemplateFormatVersion: "2010-09-09" Description: Set CloudWatch Alarms # ------------------------------------------------------------# # Input Parameters # ------------------------------------------------------------# Parameters: Ec2InstanceId: Description: Ec2 InstanceId Type: AWS::EC2::Instance::Id # ------------------------------------------------------------# # Create CloudWatch Alarms # ------------------------------------------------------------# Resources: C2CPUUtilizationAlarm: Type: AWS::CloudWatch::Alarm Properties: AlarmActions: - { "Fn::ImportValue": !Sub "my-sns-topic" } AlarmName: test-Alarms MetricName: CPUUtilization Namespace: AWS/EC2 Statistic: Average Period: 60 EvaluationPeriods: 2 Threshold: 40 TreatMissingData: breaching OKActions: - { "Fn::ImportValue": !Sub "my-sns-topic" } ComparisonOperator: GreaterThanOrEqualToThreshold Dimensions: - Name: InstanceId Value: !Ref Ec2InstanceId
마지막으로 CloudWatch Alarms를 생성합니다.
간단하게 CPU 사용률이 40%를 넘기면 이메일로 통지가 가는 설정입니다.
CloudWatch Alarms에 관한 상세한 속성은 아래 링크를 참고해 주세요.
CloudWatch Alarms를 생성할 때, EC2 인스턴스를 선택해야 하는데, CPU 사용률을 감시하고 싶은 EC2 인스턴스를 선택합니다.
이메일로 알람 받아보기
sudo amazon-linux-extras install epel -y sudo yum install stress -y sudo stress --cpu 1 --timeout 600
stress를 사용해서 강제로 CPU 사용률을 올립니다. 600초 동안 1개의 CPU를 최대한 사용합니다.
CloudWatch Alarms에서 확인해 보면 40%를 넘겼기 때문에 경보 상태가 된 것을 확인할 수 있습니다.
로그를 살펴보면, 정상에서 경보 상태로 변경된 다음, 지정해 둔 SNS Topic가 실행된 것을 확인할 수 있습니다.
이메일로 들어가 보면, 다음과 같이 알람이 온 것을 확인할 수 있습니다.
stress를 중지시키고 기다려보면 다시 CPU가 40% 밑으로 떨어지면서 status가 알람에서 OK 상태로 변경 됐다는 메일이 날아오는 것을 확인할 수 있습니다.
그 외 EC2 인스턴스 CPU 사용률을 Slack으로 받아보는 내용에 대해서는 아래 블로그를 참고해 주세요.
본 블로그 게시글을 읽고 궁금한 사항이 있으신 분들은 [email protected]로 보내주시면 감사하겠습니다.